﻿using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE80;
using EnvDTE;
using Microsoft.VisualStudio.VCProjectEngine;
using System.IO;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio;
using VSLangProj80;
using VSLangProj;

namespace Gauss.Isp
{
    /// <summary>
    /// Class that compiles the project for ISP, and launches the isp.exe
    /// executable for model checking.
    /// </summary>
    public class IspChecker
    {
        private ToolsOptions _toolsOptions;
        private DTE2 _dte;
        private int _exitCode;
        private TimeSpan _elapsedTime;
        private DateTime _startTime;
        private string _logFile;
        private int _numberProcesses;
        private int _numberInterleavings;
        private bool _hasRun;
        private bool _isRunning;
        private Dictionary<string, ProjectModification> _projectModifications;
        private List<Project> _projectsList;
        
        public bool HasRun
        {
            get { return _hasRun; }
        }

        public bool IsRunning
        {
            get { return _isRunning; }
        }

        public int ExitCode
        {
            get { return _hasRun ? _exitCode : -1; }
        }

        public string ExitCodeString
        {
            get
            {
                if (!_hasRun)
                {
                    return Resources.IspStatus_NotRun;
                }

                switch (_exitCode)
                {
                    case 0: return Resources.IspExitCode_0;
                    case 1: return Resources.IspExitCode_1;
                    case 2: return Resources.IspExitCode_2;
                    case 3: return Resources.IspExitCode_3;
                    case 4: return Resources.IspExitCode_4;
                    case 5: return Resources.IspExitCode_5;
                    case 11: return Resources.IspExitCode_11;
                    case 12: return Resources.IspExitCode_12;
                    case 13: return Resources.IspExitCode_13;
                    case 14: return Resources.IspExitCode_14;
                    case 15: return Resources.IspExitCode_15;
                    case 16: return Resources.IspExitCode_16;
                    case 17: return Resources.IspExitCode_17;
                    case 20: return Resources.IspExitCode_20;
                    case 21: return Resources.IspExitCode_21;
                    case 22: return Resources.IspExitCode_22;
                    default: return Utils.StringFormat(Resources.IspEditCode_Unknown, _exitCode);
                }
            }
        }

        public TimeSpan ElapsedTime
        {
            get
            {
                if (_isRunning && _startTime != DateTime.MinValue)
                    return DateTime.Now - _startTime;
                else if (_isRunning && _startTime == DateTime.MinValue)
                    return TimeSpan.Zero;
                else if (_hasRun)
                    return _elapsedTime;
                else
                    return TimeSpan.Zero;
            }
        }

        public string LogFile
        {
            get { return _hasRun ? _logFile : null; }
        }

        public int NumberProcesses
        {
            get { return _hasRun || _isRunning ? _numberProcesses : -1; }
        }

        public int NumberInterleavings
        {
            get { return _hasRun || _isRunning ? _numberInterleavings : -1; }
        }

        public bool DeadLock
        {
            get { return _hasRun ? _exitCode == 1 : false; }
        }

        public List<Project> ProjectsList
        {
            get { return _projectsList; }
        }

        public IspChecker(ToolsOptions defaults, DTE2 dte)
        {
            if (defaults == null)
            {
                throw new ArgumentNullException("defaults");
            }
            this._toolsOptions = defaults;

            if (dte == null)
            {
                throw new ArgumentNullException("dte");
            }
            this._dte = dte;

            this._exitCode = -1;
            this._logFile = null;
            this._numberProcesses = -1;
            this._hasRun = false;
            this._isRunning = false;
            this._projectModifications = new Dictionary<string, ProjectModification>();
        }

        public void RemoveLogFile()
        {
            if (_logFile != null && File.Exists(_logFile))
            {
                try
                {
                    File.Delete(_logFile);
                }
                catch { }
            }
            _logFile = null;
            _hasRun = false;
        }

        //TODO: this should be reworked to be cleaner wrt c# projects 
        //(always have to build the way its constructed now)
        public bool Run(int numberProcesses) 
        {
            using (BuildEventHandler buildEvents = new BuildEventHandler())
            {
                buildEvents.StopBuild = true;

                string mpiHPath = null;
                string assertHPath = null;
                string createdDirectoryPath = null;
                string logFilePath = null;
                List<Project> projectsList = null;
                bool haveCCode = false;

                _numberInterleavings = 0;
                _numberProcesses = numberProcesses;
                _isRunning = true;
                RemoveLogFile();

                try
                {  
                    // Get the projects needed to be compiled.
                    projectsList = ConfigurationDialog.GetCheckedProjects(_dte.Solution);
                    if (projectsList.Count == 0)
                    {
                        throw new ArgumentException(Resources.Message_NoStartupProject);
                    }

                    foreach (Project p in projectsList) 
                    {
                        switch (ProjectHandler.GetProjType(p))
                        {
                            case ProjectHandler.ProjType.CS:
                            case ProjectHandler.ProjType.FS:
                                break;
                            case ProjectHandler.ProjType.VC:
                                haveCCode = true;
                                break;
                            case ProjectHandler.ProjType.UNKN:
                                throw new ApplicationException(Resources.Message_NoCOrDotNetProjects);
                        }
                    }

                    // Get all of the ISP file paths.
                    FileInfo ispExecutablePath = _toolsOptions.Get_IspExecutablePath;
                    if (ispExecutablePath == null)
                    {
                        throw new FileNotFoundException(Utils.StringFormat(Resources.Message_CannotFindFile, "isp.exe"));
                    }
                    FileInfo mpiDLLpath = _toolsOptions.Get_MPIdotNET_ISP_Path;
                    FileInfo ispLibraryPath = _toolsOptions.Get_IspLibraryPath;
                    FileInfo ispHeaderPath = _toolsOptions.Get_IspHeaderPath;
                    string ispDllDirectory = _toolsOptions.Get_IspDllDirectory;

                    if (haveCCode)
                    {
                        if (ispLibraryPath == null)
                        {
                            throw new FileNotFoundException(Utils.StringFormat(Resources.Message_CannotFindFile, "isp.lib"));
                        }
                        if (ispHeaderPath == null)
                        {
                            throw new FileNotFoundException(Utils.StringFormat(Resources.Message_CannotFindFile, "isp.h"));
                        }
                    }
                    else
                    {
                        if (mpiDLLpath == null)
                        {
                            throw new FileNotFoundException(Utils.StringFormat(Resources.Message_CannotFindFile, "MPI.dll"));
                        }
                    }

                    ProjectHandler startupProject = new ProjectHandler(projectsList[0]);
                    List<ProjectHandler> projects = new List<ProjectHandler>(projectsList.Count - 1);
                    for (int i = 1; i < projectsList.Count; i++)
                    {
                        projects.Add(new ProjectHandler(projectsList[i]));
                    }

                    // If there are projects to compile...
                    DirectoryInfo tempDirectory = null;
                    if (projects.Count > 0)
                    {
                        if (haveCCode)
                        {
                            // Find mpi.h in the project's include path.
                            string mpiHeaderPath = startupProject.FindHeaderPath("mpi.h"); 
                            if (mpiHeaderPath == null)
                            {
                                mpiHeaderPath = Path.Combine(Environment.GetEnvironmentVariable("CCP_INC"), "mpi.h");
                                if (!File.Exists(mpiHeaderPath))
                                    throw new FileNotFoundException(Resources.Message_MpiHPathNotFound);
                            }

                            // Create a temporary directory for the isp.h file.
                            tempDirectory = GetTempDirectory();
                            if (!tempDirectory.Exists)
                            {
                                throw new DirectoryNotFoundException(Utils.StringFormat(Resources.Message_TempDirectoryNotExist, tempDirectory.FullName));
                            }
                            if (tempDirectory.FullName.ToUpper() != Path.GetTempPath().ToUpperInvariant())
                            {
                                createdDirectoryPath = tempDirectory.FullName;
                            }

                            // Create a mpi.h file in the new directory, including the found mpi.h file.
                            using (StreamWriter mpiH = new StreamWriter(Path.Combine(tempDirectory.FullName, "mpi.h")))
                            {
                                mpiH.WriteLine("#include \"" + mpiHeaderPath + "\"");
                                mpiH.WriteLine("#include \"" + ispHeaderPath.FullName + "\"");
                            }
                            mpiHPath = Path.Combine(tempDirectory.FullName, "mpi.h");

                            
                            // Create a assert.h file also, so we can trap the asserts in to ISP.
                            using (StreamWriter assertH = new StreamWriter(Path.Combine(tempDirectory.FullName, "assert.h")))
                            {
                                assertH.WriteLine(@"#undef  assert");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"#ifdef  NDEBUG");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"#define assert(expr)    ((void)0)");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"#else");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"#ifdef  __cplusplus");
                                assertH.WriteLine(@"extern ""C"" {");
                                assertH.WriteLine(@"#endif");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"extern void ISP_Assert_fail (const char *assertion, const char *function, const char *file, int line);");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"#ifdef  __cplusplus");
                                assertH.WriteLine(@"}");
                                assertH.WriteLine(@"#endif");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"#define assert(expr) (void)( (!!(expr)) || (ISP_Assert_fail (#expr, __FUNCTION__, __FILE__, __LINE__), 0) )");
                                assertH.WriteLine(@"");
                                assertH.WriteLine(@"#endif  /* NDEBUG */");
                            }
                            assertHPath = Path.Combine(tempDirectory.FullName, "assert.h");
                        }
                    }
                    // See which projects need to be re-built.
                    _dte.ExecuteCommand("File.SaveAll", "");
                    foreach (ProjectHandler ph in projects)
                    {
                        if (_projectModifications.ContainsKey(ph.UniqueName))
                        {
                            if (_projectModifications[ph.UniqueName].ProjectChanged())
                            {
                                ph.BuildProject = true;
                            }
                            else
                            {
                                ph.BuildProject = false;
                            }
                        }
                        else
                        {
                            _projectModifications.Add(ph.UniqueName, ph.GetProjectModification());
                            ph.BuildProject = true;
                        }
                    }

                    // Rebuild the project.
                    string backupDir = Path.Combine(startupProject.GetWorkingDirectory(), @"backupDir");
                    try
                    {   
                        // Set all of the compiler and linker paths correctly.
                        foreach (ProjectHandler ph in projects)
                        {
                            if (ph.BuildProject && haveCCode)
                            {
                                ph.AddCompilerIncludeDirectory(tempDirectory.FullName);
                                ph.AddLinkerLibraryDirectory(ispLibraryPath.Directory.FullName);
                                ph.AddLinkerDependency(ispLibraryPath.Name);
                                switch (ph.RuntimeLibrary)
                                {
                                    case runtimeLibraryOption.rtMultiThreadedDebugDLL:
                                        ph.ChangeRuntimeLibrary(runtimeLibraryOption.rtMultiThreadedDebug);
                                        break;
                                    case runtimeLibraryOption.rtMultiThreadedDLL:
                                        ph.ChangeRuntimeLibrary(runtimeLibraryOption.rtMultiThreaded);
                                        break;
                                }
                            }
                            else if (!haveCCode)
                            {
                                BackupDirectory(ph.GetWorkingDirectory(), backupDir);
                                ph.ReplaceReference(mpiDLLpath);
                            }
                        }

                        foreach (ProjectHandler ph in projects)
                        {
                            if (!ph.BuildProject && haveCCode) continue;

                            IspBuildThread buildThread = new IspBuildThread(_dte, ph.ActiveConfigName,
                                    ph.UniqueName);
                            buildEvents.StopBuild = false;
                            buildThread.Build();
                            buildEvents.StopBuild = true;
                        }
                    }
                    finally
                    {
                        // Restore the changed options.
                        foreach (ProjectHandler ph in projects)
                        {
                            if (ph.Type != ProjectHandler.ProjType.VC)
                            {
                                ph.RestoreReferences();
                            }
                            else if(ph.BuildProject)
                            {
                                ph.RestoreCompilerIncludeDirectories(); 
                                ph.RestoreLinkerLibraryDirectories();
                                ph.RestoreLinkerDependencies();
                                ph.RestoreRuntimeLibrary();
                            }
                            ph.Save();
                        }
                    }

                    // Update the project dates after the build.
                    foreach (ProjectHandler ph in projects)
                    {
                        _projectModifications[ph.UniqueName].Update();
                    }

                    // Find the executable that is being generated.

                    string executable = startupProject.GetExecutable();

                    if (string.IsNullOrEmpty(executable))
                    {
                        throw new FileNotFoundException(Resources.Message_CouldNotFindExecutable);
                    }

                    // Get the output window pane, so ISP output can be redirected to it.
                    IVsOutputWindowPane outputWindowPane = null;
                    IVsOutputWindow outputWindow = IspPackage.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow;
                    if (outputWindow != null)
                    {
                        if (ErrorHandler.Failed(outputWindow.GetPane(ref GuidList.guidISPOutputWindowPane, out outputWindowPane)))
                        {
                            try
                            {
                                ErrorHandler.ThrowOnFailure(outputWindow.CreatePane(ref GuidList.guidISPOutputWindowPane, "ISP", 1, 1));
                                ErrorHandler.ThrowOnFailure(outputWindow.GetPane(ref GuidList.guidISPOutputWindowPane, out outputWindowPane));
                            }
                            catch
                            {
                                outputWindowPane = null;
                            }
                        }
                    }
                    if (outputWindowPane != null)
                    {
                        _dte.ToolWindows.OutputWindow.Parent.Activate();
                        outputWindowPane.Clear();
                        outputWindowPane.Activate();
                    }

                    _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_Running,
                                                             numberProcesses,
                                                             executable);

                    logFilePath = Path.GetTempFileName();

                    StringBuilder ErrOutput = new StringBuilder();

                    using (System.Diagnostics.Process process = new System.Diagnostics.Process())
                    {
                        process.StartInfo.FileName = ispExecutablePath.FullName;
                        process.StartInfo.EnvironmentVariables["Path"] += ";" + ispDllDirectory;
                        process.StartInfo.Arguments = Utils.StringFormatNoCulture("-s -z true -n {0} -p {1} {2} -r {3} {4} {5} {6} {7} {8} {9} {10} {11} --log \"{12}\" \"{13}\" {14}",
                                                                                  numberProcesses,
                                                                                  _toolsOptions.IspPort,
                                                                                  _toolsOptions.IspBlockingSends ? "--block" : "--noblock",
                                                                                  _toolsOptions.IspReportProgress,
                                                                                  _toolsOptions.IspFib ? "--fibopt" : "",
                                                                                  //_toolsOptions.IspOpenMP ? "" : "--disable-omp", //have to change format string to enable
                                                                                  _toolsOptions.ISPMpicalls ? "--mpicalls" : "",
                                                                                  _toolsOptions.VerboseMode ? "-O" : "",
                                                                                  _toolsOptions.ProgOutOnly ? "-P" : "",
                                                                                  _toolsOptions.ExpMode != null &&
                                                                                  _toolsOptions.ExpMode != string.Empty ? "-e " + _toolsOptions.ExpMode : "",
                                                                                  _toolsOptions.BoundMix ? "-B" : "",
                                                                                  _toolsOptions.AmpleSetFixDisable ? "-a" : "",
                                                                                  //_toolsOptions.FocusPlus ? "-F" : "", //have to change format string to enable
                                                                                  _toolsOptions.ExpSome > 0 ? "-y " + _toolsOptions.ExpSome : "",
                                                                                  logFilePath,
                                                                                  executable,
                                                                                  startupProject.GetProgramArguments());
                        process.StartInfo.WorkingDirectory = startupProject.GetWorkingDirectory();
                        process.StartInfo.RedirectStandardInput = false;
                        process.StartInfo.UseShellExecute = false;
                        process.StartInfo.CreateNoWindow = true;

                        if (outputWindowPane != null)
                        {
                            process.StartInfo.RedirectStandardOutput = true;
                            process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(
                                delegate(object sender, System.Diagnostics.DataReceivedEventArgs e)
                                {
                                    if (e.Data != null && e.Data.StartsWith("INTERLEAVING :"))
                                    {
                                        Utils.TryParseInt(e.Data.Substring("INTERLEAVING :".Length), out _numberInterleavings);
                                    }
                                    outputWindowPane.OutputString(" ISP> " + e.Data + Environment.NewLine);
                                });

                            process.StartInfo.RedirectStandardError = true;
                            process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(
                                    delegate(object sender, System.Diagnostics.DataReceivedEventArgs e)
                                    {
                                        ErrOutput.Append("PROG> " + e.Data + Environment.NewLine);
                                        //outputWindowPane.OutputString("PROG> " + e.Data + Environment.NewLine);
                                    });
                        }
                        else //not sure what this branch does
                        {
                            process.StartInfo.RedirectStandardOutput = true;
                            process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(
                                delegate(object sender, System.Diagnostics.DataReceivedEventArgs e)
                                {
                                    if (e.Data != null && e.Data.StartsWith("INTERLEAVING :"))
                                    {
                                        Utils.TryParseInt(e.Data.Substring("INTERLEAVING :".Length), out _numberInterleavings);
                                    }
                                });

                            process.StartInfo.RedirectStandardError = false;
                        }

                        _startTime = DateTime.Now;
                        process.Start();
                        process.BeginOutputReadLine();
                        if (outputWindowPane != null)
                        {
                            process.BeginErrorReadLine();
                        }
                        try
                        {
                            process.WaitForExit(int.MaxValue - 1);
                            //process.WaitForExit();
                        }
                        catch (System.Threading.ThreadAbortException)
                        {
                            KillProcess.TerminateProcessTree(process.Handle, (uint)process.Id, 255);
                            throw;
                        }
                        finally
                        {
                            while (!process.HasExited) //TODO: switch back to if?
                            {
                                process.WaitForExit(500);
                            }
                            //these (below) are causing InvalidOpEx: cannot mix sync & async ops on proc stream
                            //TODO: fix this hack
                            try
                            {
                                process.StandardError.BaseStream.Flush();
                            }
                            catch (System.InvalidOperationException) { }
                            try
                            {
                                process.StandardOutput.BaseStream.Flush();
                            }
                            catch (System.InvalidOperationException) { }
                            process.CancelOutputRead();
                            process.CancelErrorRead();
                        }
                        KillProcess.TerminateProcessTree(process.Handle, (uint)process.Id, 255);
                        _elapsedTime = DateTime.Now - _startTime;
                        _startTime = DateTime.MinValue;
                        _exitCode = process.ExitCode;
                        //process.CancelOutputRead();
                        //process.CancelErrorRead();
                        if (outputWindowPane != null)
                        {
                            outputWindowPane.OutputString(ErrOutput.ToString());
                        }
                        if (!haveCCode)
                        {

                            foreach (string file in Directory.EnumerateFiles(startupProject.GetWorkingDirectory()))
                            {
                                try
                                {
                                    File.Delete(file);
                                }
                                catch (Exception) { }
                            }
                            RestoreDirectory(startupProject.GetWorkingDirectory(), backupDir);
                        }
                    }
                }
                catch (System.Threading.ThreadAbortException)
                {
                    _dte.StatusBar.Text = Resources.IspStatus_StatusBar_Aborted;
                    throw;
                }
                catch (Exception exception)
                {
                    RemoveLogFile();
                    _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_Error, numberProcesses, exception.Message);
                    throw;
                }
                finally
                {
                    _isRunning = false;
                    if (mpiHPath != null && File.Exists(mpiHPath)) 
                    {
                        try
                        {
                            File.Delete(mpiHPath);
                        }
                        catch
                        {
                        }
                        mpiHPath = null;
                    }
                    if (assertHPath != null && File.Exists(assertHPath))
                    {
                        try
                        {
                            File.Delete(assertHPath);
                        }
                        catch
                        {
                        }
                        assertHPath = null;
                    }
                    if (createdDirectoryPath != null && Directory.Exists(createdDirectoryPath))
                    {
                        try
                        {
                            Directory.Delete(createdDirectoryPath);
                        }
                        catch
                        {
                        }
                        createdDirectoryPath = null;
                    }
                }

                _hasRun = true;
                _logFile = logFilePath;
                _projectsList = projectsList;
                switch (_exitCode)
                {
                    case 0:
                        _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_NoDeadlocks, numberProcesses);
                        break;
                    case 1:
                        _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_Deadlock, numberProcesses);
                        break;
                    case 3:
                        _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_AssertionViolation, numberProcesses);
                        break;
                    case 4:
                        _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_Abort, numberProcesses);
                        break;
                    case 5:
                        _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_InvalidRank, numberProcesses);
                        break;
                    default:
                        _dte.StatusBar.Text = Utils.StringFormat(Resources.IspStatus_StatusBar_Error, numberProcesses, ExitCodeString);
                        break;
                }
                if (_exitCode == 0 || _exitCode == 1 || _exitCode == 3)
                    return true;
                else
                    return false;
            }
        }

        public static void BackupDirectory(string sourceDir, string destDir)
        {
            if (Directory.Exists(destDir))
            {
                Directory.Delete(destDir, true);
            }
            Directory.CreateDirectory(destDir);
            foreach (string file in Directory.EnumerateFiles(sourceDir))
            {
                File.Copy(file, Path.Combine(destDir, Path.GetFileName(file)));
            }
        }

        public static void RestoreDirectory(string destDir, string backupDir)
        {
            foreach (string file in Directory.EnumerateFiles(backupDir))
            {
                string newFile = Path.Combine(destDir, Path.GetFileName(file));
                if (File.Exists(newFile))
                {
                    try
                    {
                        File.Delete(newFile);
                    }
                    catch (Exception) { }
                }
                else
                {
                    File.Copy(file, newFile);
                }
            }
            Directory.Delete(backupDir, true);
        }



        /// <summary>
        /// This function simply creates a directory with a random name in the
        /// user's temporary folder. It returns the directory's path.
        /// </summary>
        /// <returns>The random temporary directory's path. Null if there was an error.</returns>
        private DirectoryInfo GetTempDirectory()
        {
            if (File.Exists(Path.Combine(Path.GetTempPath(), "mpi.h")) || File.Exists(Path.Combine(Path.GetTempPath(), "assert.h")))
            {
                string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                return Directory.CreateDirectory(path);
            }
            else
            {
                return new DirectoryInfo(Path.GetTempPath());
            }
        }

        private class BuildEventHandler : IVsUpdateSolutionEvents, IDisposable
        {
            IVsSolutionBuildManager2 _buildManager;
            uint _cookie;
            private bool _stopBuild;

            public BuildEventHandler()
            {
                _buildManager = IspPackage.GetGlobalService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager2;
                if (_buildManager != null)
                    _buildManager.AdviseUpdateSolutionEvents(this, out _cookie);
            }

            public bool StopBuild
            {
                get { return _stopBuild; }
                set { _stopBuild = value; }
            }

            #region IVsUpdateSolutionEvents Members

            public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy)
            {
                return VSConstants.S_OK;
            }

            public int UpdateSolution_Begin(ref int pfCancelUpdate)
            {
                pfCancelUpdate = _stopBuild ? 1 : 0;
                return VSConstants.S_OK;
            }

            public int UpdateSolution_Cancel()
            {
                return VSConstants.S_OK;
            }

            public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand)
            {
                return VSConstants.S_OK;
            }

            public int UpdateSolution_StartUpdate(ref int pfCancelUpdate)
            {
                return VSConstants.S_OK;
            }

            #endregion

            #region IDisposable Members

            ~BuildEventHandler()
            {
                Dispose(false);
            }

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            protected virtual void Dispose(bool disposing)
            {
                if (disposing)
                {
                    if (_cookie != 0 && _buildManager != null)
                    {
                        _buildManager.UnadviseUpdateSolutionEvents(_cookie);
                        _cookie = 0;
                    }
                }
            }

            #endregion
        }
    }
}
